package cn.layfolk.seckill.service.impl;

import cn.hutool.json.JSONUtil;
import cn.layfolk.daimao.vo.TGoods;
import cn.layfolk.daimao.vo.respvo.ResponseVo;
import cn.layfolk.daimao.web.feign.GoodsFeignApi;
import cn.layfolk.seckill.domain.TSeckillGoods;
import cn.layfolk.seckill.mapper.TSeckillGoodsMapper;
import cn.layfolk.seckill.redis.SeckillRedisKey;
import cn.layfolk.seckill.service.TSeckillGoodsService;
import cn.layfolk.seckill.vo.SeckillGoodsVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2021-01-10
 */
@Slf4j
@Service
public class TSeckillGoodsServiceImpl extends ServiceImpl<TSeckillGoodsMapper, TSeckillGoods> implements TSeckillGoodsService {


    @Autowired
    private TSeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private GoodsFeignApi goodsFeignApi;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public SeckillGoodsVo findByIdInCache(Long seckillId) {
        String json = (String) stringRedisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_GOODS_HASH.getRealKey(""), seckillId + "");
        if (json != null) {
            SeckillGoodsVo vo = JSONUtil.toBean(json, SeckillGoodsVo.class);
            if (vo != null) {
                String str = (String) stringRedisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(""), seckillId + "");
                if (!StringUtils.isEmpty(str)) {
                    int stockCount = Integer.parseInt(str);
                    vo.setStockCount(Math.max(stockCount, 0));
                }
            }
            return vo;
        }
        return null;
    }

    @Override
    public Integer decrStockCount(TSeckillGoods vo) {
        QueryWrapper<TSeckillGoods> wrapper = new QueryWrapper<>();
        wrapper.eq("id", vo.getId()).ge("stock_count", 0);
        vo.setStockCount(vo.getStockCount() - 1);
        return seckillGoodsMapper.update(vo, wrapper);
    }

    @Override
    public SeckillGoodsVo findById(Long seckillId) {

        //根据id查询出对应的秒杀商品
        TSeckillGoods seckillGoods = seckillGoodsMapper.selectById(seckillId);
        List<SeckillGoodsVo> vos = join(Collections.singletonList(seckillGoods));
        return !CollectionUtils.isEmpty(vos) ? vos.get(0) : null;
    }

    @Override
    public Integer incrStockCount(Long seckillId) {
        return seckillGoodsMapper.incrStockCount(seckillId);
    }

    @Override
    public List<SeckillGoodsVo> query() {
        // 1.获取秒杀商品列表
        List<TSeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(null);
        // 5.最终返回组合后新的对象列表
        return join(seckillGoodsList);
    }

    @Override
    public List<SeckillGoodsVo> queryByCache() {
        // 1.获取秒杀商品列表
        List<Object> values =
                stringRedisTemplate.opsForHash().values(SeckillRedisKey.SECKILL_GOODS_HASH.getRealKey(""));
        if (CollectionUtils.isEmpty(values)) {
            return null;
        }

        List<SeckillGoodsVo> ret = new ArrayList<>();
        Map<Object, Object> stockCountMap = stringRedisTemplate.opsForHash().entries(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(""));
        for (Object value : values) {
            String json = (String) value;
            SeckillGoodsVo vo = JSONUtil.toBean(json, SeckillGoodsVo.class);
            if (vo != null) {
                String stockCountStr = (String) stockCountMap.get(vo.getId() + "");
                if (!StringUtils.isEmpty(stockCountStr)) {
                    int stockCount = Integer.parseInt(stockCountStr);
                    vo.setStockCount(Math.max(stockCount, 0));
                }
            }
            ret.add(vo);
        }
        // 5.最终返回组合后新的对象列表
        return ret;
    }

    public List<SeckillGoodsVo> join(List<TSeckillGoods> seckillGoodsList) {
        // 2.根据得到的秒杀商品列表，得到一个商品id列表 List<Long>
        Set<Long> idList = new HashSet<>(seckillGoodsList.size());
        for (TSeckillGoods seckillGoods : seckillGoodsList) {
            idList.add(seckillGoods.getGoodId());
        }
        // 3.根据得到的商品id列表，调用商品服务查询出对应的商品列表
        ResponseVo<List<TGoods>> result = goodsFeignApi.queryByIdList(idList);
        //result 可能返回的集中类型结果
        // 1. 发生服务降级，返回null
        // 2. 业务执行失败，返回RespVo<ErrorCode>
        // 3. 如果业务正常执行完毕，此时返回RespVo<List<Goods>>
        if (result == null || result.getStatusCode() != 200) {
            log.error("获取商品错误信息，错误信息如下：{}", JSONUtil.toJsonStr(result));
            return null;
        }
        List<TGoods> goodsList = result.getData();
        //增加一个临时缓存对象，key = 商品id，value=商品对象
        Map<Long, TGoods> tempCache = new HashMap<>(goodsList.size());
        tempCache = goodsList.stream().collect(Collectors.toMap(TGoods::getId, goods -> goods));

        // 4.遍历秒杀商品列表，将匹配的普通商品信息获取出来，组合成一个新的对象
        SeckillGoodsVo vo;
        List<SeckillGoodsVo> ret = new ArrayList<>(seckillGoodsList.size());
        for (TSeckillGoods seckillGoods : seckillGoodsList) {
            TGoods goods = tempCache.get(seckillGoods.getGoodId());
            if (goods != null) {
                vo = new SeckillGoodsVo();
                //直接将seckillGoods对象中的属性全部copy到vo对象中
                BeanUtils.copyProperties(seckillGoods, vo);
                //直接将goods对象中的全部属性copy到vo对象中
                BeanUtils.copyProperties(goods, vo);
                // 将vo对象添加到结果列表中
                ret.add(vo);
            }
        }
        return ret;
    }

}
